"""
Stub generator for missing external dependency types.

This module provides utilities for emitting lightweight C++ stub definitions
when the conversion pipeline encounters references to types that are not
available in the extracted IR.
"""

from __future__ import annotations

from pathlib import Path
from typing import Dict, Iterable, Set


class StubGenerator:
    """Generate C++ stub headers for missing dependency types."""

    _TEMPLATE_TYPES: Dict[str, Iterable[str]] = {
        "WeakViewList": ("T",),
        "ArrayBase": ("T",),
        "CollectionBase": ("T",),
        "IStack": ("T",),
        "IQueue": ("T",),
    }

    _FRAMEWORK_CLASSES: Set[str] = {
        "Bucket",
        "Random",
        "GroupResult_t",
        "HttpClient",
        "WebException",
        "JObject",
        "JToken",
        "JArray",
    }

    def generate_stubs(self, missing_types: Set[str]) -> Dict[str, str]:
        """Generate stub definitions for all missing types."""
        stubs: Dict[str, str] = {}

        for type_name in sorted(missing_types):
            generator = getattr(self, f"_generate_{type_name.lower()}_stub", None)
            if callable(generator):
                stub_code = generator()
            elif type_name == "Task":
                stub_code = self._generate_task_stub()
            elif type_name in self._TEMPLATE_TYPES:
                params = ", ".join(f"typename {p}" for p in self._TEMPLATE_TYPES[type_name])
                stub_code = self._generate_template_class_stub(type_name, params)
            elif type_name in self._FRAMEWORK_CLASSES:
                stub_code = self._generate_framework_class_stub(type_name)
            elif type_name in {"Func", "Action", "FuncRef", "ActionRef"}:
                stub_code = self._generate_delegate_stub(type_name)
            else:
                stub_code = self._generate_simple_class_stub(type_name)

            if stub_code:
                stubs[type_name] = stub_code.strip()

        return stubs

    def write_stubs_file(self, stubs: Dict[str, str], output_path: Path) -> None:
        """Write stub definitions to the specified header file."""
        preamble = (
            "// Auto-generated stub declarations\n"
            "// Generated by the C# to C++ conversion pipeline\n\n"
            "#pragma once\n\n"
            "#include <chrono>\n"
            "#include <cstdint>\n"
            "#include <future>\n"
            "#include <stdexcept>\n"
            "#include <string>\n"
        )

        body_lines = [preamble]
        for type_name in sorted(stubs.keys()):
            body_lines.append(stubs[type_name])
            body_lines.append("\n")

        output_path.write_text("".join(body_lines).rstrip() + "\n")

    # ---------------------------------------------------------------------#
    # Stub helpers
    # ---------------------------------------------------------------------#

    def _generate_task_stub(self) -> str:
        """Generate Task<T> stub using std::future."""
        return """
// Task<T> stub using std::future
template<typename TResult>
class Task {
public:
    Task() = default;
    explicit Task(std::future<TResult> future)
        : m_future(std::move(future)) {}

    TResult get() {
        return m_future.get();
    }

    bool is_ready() const {
        return m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
    }

private:
    std::future<TResult> m_future;
};

// Task<void> specialization
template<>
class Task<void> {
public:
    Task() = default;
    explicit Task(std::future<void> future)
        : m_future(std::move(future)) {}

    void get() {
        m_future.get();
    }

    bool is_ready() const {
        return m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
    }

private:
    std::future<void> m_future;
};
""".strip()

    def _generate_template_class_stub(self, class_name: str, template_params: str) -> str:
        """Generate a minimal template class stub."""
        return f"""
// Stub for {class_name}<{template_params}>
template<{template_params}>
class {class_name} {{
public:
    {class_name}() = default;
    virtual ~{class_name}() = default;
}};
""".strip()

    def _generate_framework_class_stub(self, class_name: str) -> str:
        """Generate stub for known framework classes."""
        if class_name == "WebException":
            return """
// Stub for WebException
class WebException : public std::runtime_error {
public:
    explicit WebException(const std::string& message)
        : std::runtime_error(message) {}
};
""".strip()

        if class_name == "HttpClient":
            return """
// Stub for HttpClient
class HttpClient {
public:
    HttpClient() = default;

    Task<HttpResponse> get(const std::string& /*url*/) {
        return Task<HttpResponse>{};
    }

    Task<HttpResponse> post(const std::string& /*url*/, const std::string& /*body*/) {
        return Task<HttpResponse>{};
    }
};
""".strip()

        return self._generate_simple_class_stub(class_name)

    def _generate_simple_class_stub(self, class_name: str) -> str:
        """Generate a simple class stub."""
        return f"""
// Stub for {class_name}
class {class_name} {{
public:
    {class_name}() = default;
    virtual ~{class_name}() = default;

    {class_name}(const {class_name}&) = default;
    {class_name}& operator=(const {class_name}&) = default;
    {class_name}({class_name}&&) noexcept = default;
    {class_name}& operator=({class_name}&&) noexcept = default;
}};
""".strip()

    def _generate_delegate_stub(self, delegate_name: str) -> str:
        """Generate variadic delegate stub used when mapping does not convert."""
        return f"""
// Stub for {delegate_name}<...>
template<typename... TArgs>
class {delegate_name} {{
public:
    {delegate_name}() = default;
}};
""".strip()
